package com.nulldev.util.logging;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import com.nulldev.util.JVM.Exceptions;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.io.ColouredConsole.ConsoleColours;
import com.nulldev.util.io.ColouredConsole.ConsoleColours.ConsoleTextColours;
import com.nulldev.util.io.FastIO.FastIO;
import com.nulldev.util.io.FastIO.FastIO.IOSubmission;
import com.nulldev.util.manage.Version;

public class Logger {

	/*
	 * nullUtil's Logger API
	 * 
	 * TODO: - Implement LoggerLook.v3_SWIFT - Reduce latency between FastIO and
	 * submission
	 */

	public static final Version version = new Version("1.0.4");
	public static final Logger instance = new Logger("generic");

	public final AtomicBoolean infoEnabled = new AtomicBoolean(true);
	public final AtomicBoolean warnEnabled = new AtomicBoolean(true);
	public final AtomicBoolean errorEnabled = new AtomicBoolean(true);
	public final AtomicBoolean debugEnabled = new AtomicBoolean(false);
	public final AtomicBoolean traceEnabled = new AtomicBoolean(false);
	public final AtomicBoolean treatLogAsDebug = new AtomicBoolean(false);

	public static boolean useColour() {
		final String TERM = System.getenv().get("TERM");
		if (TERM != null && TERM.equalsIgnoreCase("nullutil-force-enable-color")) {
			return true;
		} else {
			return System.console() != null && TERM != null;
		}
	}

	private final AtomicReference<LoggerLook> look = new AtomicReference<LoggerLook>(useColour() ? LoggerLook.v3 : LoggerLook.v2);
	private final AtomicReference<LoggerFormattingMode> formattingMode = new AtomicReference<LoggerFormattingMode>(LoggerFormattingMode.LEGACY_FORMATTING);

	public final AtomicBoolean NEW_LOOK_DISPLAY_CLASS_NAME = new AtomicBoolean(true);

	public LoggerLook getLook() {
		return this.look.get();
	}

	public void setLook(final LoggerLook look) {
		if (look == null)
			return;
		this.look.lazySet(look);
	}

	public LoggerFormattingMode getFormattingMode() {
		return this.formattingMode.get();
	}

	public void setFormattingMode(final LoggerFormattingMode mode) {
		if (mode == null)
			return;
		this.formattingMode.lazySet(mode);
	}

	private final AtomicReference<String> className = new AtomicReference<String>();

	public Logger(final String className) {
		this.className.set(className);
	}

	private static String generate_v3_string(final ConsoleTextColours color, final String prefix) {
		return color.text() + prefix + ConsoleColours.ANSI_RESET;
	}

	private String generate_v3_classname_string() {
		return " " + ConsoleTextColours.GRAY.text() + "[" + this.className.get() + "]" + ConsoleColours.ANSI_RESET;
	}

	private String makeString(final Object o) {
		return makeString(o, this.formattingMode.get());
	}

	private static String makeString(final Object o, final LoggerFormattingMode mode) {
		if (o == null)
			return "null";
		switch (mode) {
			default:
			case LEGACY_FORMATTING: {
				if (o instanceof String)
					return ((String) o);
				else if (o instanceof Throwable)
					return Exceptions.toString((Throwable) o);
				else if (o instanceof char[])
					return StringsUtil.join((char[]) o, ',');
				else if (o instanceof Enumeration<?>)
					return StringsUtil.join((Enumeration<?>) o, ',');
				else if (ArrayUtils.isArray(o))
					return Arrays.toString((Object[]) o);
				else
					return String.valueOf(o);
			}
			case NO_FORMATTING: {
				return (o instanceof String ? (String) o : String.valueOf(o));
			}
		}
	}

	private String makeString(final String s, final Object... o) {
		return makeString(this.formattingMode.get(), s, o);
	}

	private static String makeString(final LoggerFormattingMode mode, final String s, final Object... o) {
		if (s == null)
			return "null";
		switch (mode) {
			default:
			case LEGACY_FORMATTING: {
				if (!s.contains("%"))
					return s + " " + Arrays.toString(o);
				if (o == null || o.length == 0)
					return makeString(s, mode);
				if (o.length == 1 && o[0] instanceof Throwable)
					return s + JVM.lineSeperator() + Exceptions.toString((Throwable) o[0]);
				final String first_pass = String.format(s, o);
				if (first_pass.equals(s))
					return s + " " + Arrays.toString(o);
				return first_pass;
			}
			case NO_FORMATTING:
				return s;
		}
	}

	public void info(final Object _string) {
		this.info(_string, false);
	}

	public void info(final Object _string, final boolean disableClassPrinting) {
		if (!this.infoEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(final PrintWriter OUTPUT, final PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [INFO @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT.println(
							"[i] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() && !disableClassPrinting ? "[" + Logger.this.className.get() + "] " : "")
									+ string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT.println(
							"[∗] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() && !disableClassPrinting ? "[" + Logger.this.className.get() + "] " : "")
									+ string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.BRIGHT_GREEN, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "*" : "i"))
							+ "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() && !disableClassPrinting) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public boolean log(final java.util.logging.Level fine, String string, Object... objects) {
		final Level localLevel;
		if (fine == java.util.logging.Level.ALL || fine == java.util.logging.Level.FINER || fine == java.util.logging.Level.FINEST) {
			localLevel = Level.FINEST;
		} else if (fine == java.util.logging.Level.FINE) {
			localLevel = Level.FINE;
		} else if (fine == java.util.logging.Level.SEVERE || fine == java.util.logging.Level.WARNING) {
			localLevel = Level.WARNING;
		} else if (fine == java.util.logging.Level.CONFIG || fine == java.util.logging.Level.INFO) {
			localLevel = Level.INFO;
		} else {
			throw new UnsupportedOperationException("Unknown level: " + fine);
		}
		return this.log(localLevel, string, objects);
	}

	public boolean log(Level fine, String string, Object... objects) {
		if (fine == Level.SEVERE) {
			this.error(string, objects);
		} else if (fine == Level.FINEST) {
			this.trace(string, objects);
		} else if (fine == Level.FINE) {
			this.debug(string, objects);
		} else if (fine == Level.WARNING) {
			this.warn(string, objects);
		} else if (fine == Level.INFO) {
			this.info(string, objects);
		}
		return true;
	}

	public void warn(final Object _string) {
		if (!this.warnEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Warning @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT.println("[!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT.println("[W] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.YELLOW, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "W" : "!")) + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void dwarn(final Object _string) {
		if (!this.warnEnabled.get() || !this.debugEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Warning @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT.println("[!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT.println("[W] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.YELLOW, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "W" : "!")) + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void warn(final String _string, final Object... objects) {
		if (!this.warnEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, objects);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Warning @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT.println("[W] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT.println("[!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.YELLOW, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "W" : "!")) + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void error(final Object _string) {
		if (!this.errorEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT_ERROR.println("[nullLogger] [Error @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT_ERROR.println("[!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT_ERROR.println("[!!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.RED, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "!" : "!!")) + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT_ERROR.println(str);
				}
			}
		});
	}

	public void derror(final Object... objects) {
		if (!this.errorEnabled.get() || !this.debugEnabled.get()) {
			return;
		}
		this.error(objects);
	}

	public void derror(final String s, final Throwable t) {
		if (!this.errorEnabled.get() || !this.debugEnabled.get()) {
			return;
		}
		this.error(s, t);
	}

	public void derror(final Object _string) {
		if (!this.errorEnabled.get() || !this.debugEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT_ERROR.println("[nullLogger] [Error @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT.println("[!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT_ERROR.println("[!!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.RED, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "!" : "!!")) + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void error(String _string, Object... values) {
		if (!this.errorEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, values);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT_ERROR.println("[nullLogger] [Error @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT.println("[!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT_ERROR.println("[!!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.RED, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "!" : "!!")) + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void error(final String string, final Throwable value) {
		if (!this.errorEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT_ERROR.println("[nullLogger] [Error @ " + Logger.this.className.get() + "] " + string);
					OUTPUT_ERROR.println(Exceptions.toString(value));
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT_ERROR.println("[!!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
					OUTPUT_ERROR.println(Exceptions.toString(value));
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.RED, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "!" : "!!")) + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					str += "\n";
					str += Exceptions.toString(value);
					OUTPUT_ERROR.println(str);
				}
			}
		});
	}

	public void debug(final Object _string) {
		if (!this.debugEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Debug @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT.println("[D] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.WHITE, "D") + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void debug(final String _string, Object... objects) {
		if (!this.debugEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, objects);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Debug @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT.println("[D] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.WHITE, "D") + "] ";
					str += string;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void trace(String string) {
		if (!this.traceEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Trace @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2_SWIFT)) {
					OUTPUT.println("[T] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					OUTPUT.println("[T] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.WHITE, "T") + "] ";
					str += ConsoleTextColours.WHITE.text() + string + ConsoleColours.ANSI_RESET;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public boolean isErrorEnabled() {
		return this.errorEnabled.get();
	}

	public void error(final Throwable e) {
		if (!this.errorEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT_ERROR.println("[nullLogger] [Error @ " + Logger.this.className.get() + "] " + e.getMessage() + " [" + e.getClass().getName() + "]");
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					// FIXME: MISSING V2_SWIFT
					OUTPUT_ERROR.println("[D] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : ""));
					OUTPUT_ERROR.println(Exceptions.toString(e));
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.RED, (Logger.this.look.get().equals(LoggerLook.v3_SWIFT) ? "!" : "!!")) + "]";
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					str += "\n";
					str += Exceptions.toString(e);
					OUTPUT_ERROR.println(str);
				}
			}
		});
	}

	public void trace(final String _string, final String val) {
		if (!this.traceEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, val);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Trace @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					// FIXME: MISSING V2_SWIFT
					OUTPUT.println("[T] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.WHITE, "T") + "] ";
					str += ConsoleTextColours.GRAY.text() + string + ConsoleColours.ANSI_RESET;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void trace(final String _string, final Object val) {
		if (!this.traceEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, val);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Trace @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					// FIXME: MISSING V2_SWIFT
					OUTPUT.println("[T] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.WHITE, "T") + "] ";
					str += ConsoleTextColours.GRAY.text() + string + ConsoleColours.ANSI_RESET;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void trace(final String _string, final Object... vals) {
		if (!this.traceEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, vals);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Trace @ " + Logger.this.className.get() + "] " + string);
				} else if (Logger.this.look.get().equals(LoggerLook.v2)) {
					// FIXME: MISSING V2_SWIFT
					OUTPUT.println("[T] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				} else {
					String str = "[" + generate_v3_string(ConsoleTextColours.WHITE, "T") + "] ";
					str += ConsoleTextColours.GRAY.text() + string + ConsoleColours.ANSI_RESET;
					if (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get()) {
						str += generate_v3_classname_string();
					}
					OUTPUT.println(str);
				}
			}
		});
	}

	public void trace(final String string, final String val, final Exception ex) {
		if (!this.traceEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Trace @ " + Logger.this.className.get() + "] " + string + " [Value: " + val + "]");
					OUTPUT.println("[nullLogger] Printing exception...");
					OUTPUT.println(Exceptions.toString(ex));
				} else {
					// FIXME: MISSING V3_SWIFT
					OUTPUT.println(
							"[T] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string + " | " + val);
					OUTPUT.println(Exceptions.toString(ex));
				}
			}
		});
	}

	public void info(final String _string, final String val) {
		if (!this.infoEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, val);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [INFO @ " + Logger.this.className.get() + "] " + string);
				} else {
					// FIXME: MISSING V3_SWIFT
					OUTPUT.println("[i] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				}
			}
		});
	}

	public void info(final String _string, final Object... objects) {
		if (!this.infoEnabled.get() || (this.treatLogAsDebug.get() && !this.debugEnabled.get())) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				final String string = makeString(_string, objects);
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [INFO @ " + Logger.this.className.get() + "] " + string);
				} else {
					// FIXME: MISSING V3_SWIFT
					OUTPUT.println("[i] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string);
				}
			}
		});
	}

	public void error(final String string, final String val, final Throwable ex) {
		if (!this.errorEnabled.get()) {
			return;
		}
		FastIO.get().submit(new IOSubmission() {

			@Override
			public void onCallable(PrintWriter OUTPUT, PrintWriter OUTPUT_ERROR) {
				if (Logger.this.look.get().equals(LoggerLook.LEGACY)) {
					OUTPUT.println("[nullLogger] [Error @ " + Logger.this.className.get() + "] " + string + " [Value: " + val + "]");
					OUTPUT_ERROR.println("[nullLogger] Printing exception...");
					OUTPUT_ERROR.println(Exceptions.toString(ex));
				} else {
					// FIXME: MISSING V3_SWIFT
					OUTPUT.println(
							"[!!] " + (Logger.this.NEW_LOOK_DISPLAY_CLASS_NAME.get() ? "[" + Logger.this.className.get() + "] " : "") + string + " | " + val);
					OUTPUT_ERROR.println(Exceptions.toString(ex));
				}
			}
		});
	}

	public void finest(String string) {
		this.trace(string);
	}

	public boolean isLoggingEnabled() {
		return this.infoEnabled.get();
	}

	public void warning(final String string) {
		this.warn(string);
	}

	public void overrideName(final String string) {
		this.className.lazySet(string);
	}

	public void verb(final String string) {
		this.trace(string);
	}

	public static enum Level {
		SEVERE,
		FINEST,
		FINE,
		WARNING,
		INFO
	}

	public static Logger getLogger(final String name) {
		return new Logger(name);
	}

	public boolean isDebugEnabled() {
		return this.debugEnabled.get();
	}

	public static void main(String[] args) {
		JVM.println("System.console: " + System.console());
		JVM.println("$TERM: " + System.getenv().get("TERM"));
	}

	public boolean isTraceEnabled() {
		return this.traceEnabled.get();
	}

	public Logger setDebugEnabled(final boolean value) {
		this.debugEnabled.lazySet(value);
		return this;
	}

	public Logger setTraceEnabled(final boolean value) {
		this.traceEnabled.lazySet(value);
		return this;
	}

	public boolean isLoggable(final java.util.logging.Level level) {
		switch (level.intValue()) {
			case Integer.MAX_VALUE: /* OFF */
				return false;
			case 1000: /* SEVERE */
				return this.errorEnabled.get();
			case 900: /* WARNING */
				return this.warnEnabled.get();
			case 800: /* INFO */
			case 700: /* CONFIG */
				return this.infoEnabled.get();
			case 500: /* FINE */
			case 400: /* FINER */
				return this.debugEnabled.get();
			case 300: /* FINEST */
				return this.traceEnabled.get();
			case Integer.MIN_VALUE: /* ALL */
				return true;
			default:
				return true;
		}
	}

	public void fine(final String string) {
		this.debug(string);
	}

	public void finer(final String string) {
		this.debug(string);
	}
}
